#!/usr/bin/env python
# coding=UTF-8
import logging
import time
import datetime
from hashlib import md5
import urllib, urllib2
import random, json

import redisco
from redisco.containers import List, Hash
from nsq.http import nsqd

import config
import dao
import game
import paohuzi

#from pygeohash import geohash, geohash_haversine_distance
#import itertools

redisco.connection_setup(host=config.REDIS_HOST, port=config.REDIS_PORT, db=config.REDIS_DB)

def nsq_pub(topic, message):
    try:
        nsq_client = nsqd.Client("http://%s/" % config.NSQ_HTTP)
        if isinstance(message, dict):
            message = json_encode(message)
        logging.info(u"推送消息:%s" % message)
        nsq_client.pub(topic, message)
        return True
    except:
        logging.error("nsq send error", exc_info=True)
        return False
    
def json_encode(data):
    return json.dumps(data, separators=(',', ':'))

def push_room_cast(topic, rid, bid, uid, data):
    l = List("r:%s:p" % rid)
    for member in l.members:
        if member:
            nsq_pub("response", json_encode({"d":data, "u":str(member), "b":bid, "t":topic}))
    return 

def push_member_cast(topic, members, bid, uid, data):
    for member in members:
        if not member or member == str(uid):
            continue
        nsq_pub("response", json_encode({"d":data, "u":str(member), "b":bid, "t":topic}))
    return

def push_uid_msg(reader, bid, uid, data):
    nsq_pub("response", json_encode({"d":data, "u":str(uid), "b":bid, "t":reader._topic}))
    return

def online(reader, bid, uid, data):
    user_info = Hash("%s_%s" % (bid, uid))
    user_info['online'] = 'y'
    rid = user_info['rid']
    if not rid:
        rid = data.get("rid")
    if rid:
        l = List("r:%s:p" % rid)
        data = {"act":"online", "uid":uid, "rid":rid}
        push_member_cast("status", l.members, bid, uid, data)
        db = dao.Game_DB()
        online = 'y'
        db.update_player_online(rid, uid, online)
        room = Hash("r:%s" % rid)
        cid = room['cid']
        if cid:
            db.update_club_online(uid, online)
        mid = room['mid']
        if mid:
            db.update_match_online(mid, uid, online)
    return

def offline(reader, bid, uid, data):
    user_info = Hash("%s_%s" % (bid, uid))
    user_info['online'] = 'n'
    rid = user_info['rid']
    if not rid:
        rid = data.get("rid")
    if rid:
        l = List("r:%s:p" % rid)
        data = {"act":"offline", "uid":uid, "rid":rid}
        push_member_cast("status", l.members, bid, uid, data)
        db = dao.Game_DB()
        online = 'n'
        db.update_player_online(rid, uid, online)
        room = Hash("r:%s" % rid)
        cid = room['cid']
        if cid:
            db.update_club_online(uid, online)
        mid = room['mid']
        if mid:
            db.update_match_online(mid, uid, online)
            if room['game_type'] == 'pdk':
                tdh = game.PaoDeKuai(rid, bid)
                if room['stop_pop_user'] == str(uid):
                    tdh.auto_put(str(uid))
                return
            if room['game_type'] == 'zzmj':
                tdh = game.ZZMaJiang(rid, bid)
            elif room['game_type'] == 'hzmj':
                tdh = game.HZMaJiang(rid, bid)
            elif room['game_type'] == 'csmj':
                tdh = game.CSMaJiang(rid, bid)
            else:
                tdh = game.MaJiang(rid, bid)
            if room['stop_pop_user']:
                if room['stop_pop_user'] == str(uid):
                    tdh.pass_card(uid, {})
                    logging.error(u"用户离线自动pass:%s" % uid)
            if room['last_get_user'] == str(uid) and room['status'] == 'start':
                logging.error(u"用户离线自动出牌:%s" % uid)
                p = game.Player(uid, bid, rid)
                card = p.current_card.members[-1]
                tdh.put_card(uid, {"card":card})
                

def im_chat(reader, bid, uid, data):
    user_info = Hash("%s_%s" % (bid, uid))
    rid = user_info['rid']
    if rid:
        data['uid'] = uid
        data['rid'] = rid
        push_room_cast("im", rid, bid, uid, data)
    return

def create_room(reader, bid, uid, data):
    rid = data['rid']
    l = List("r:%s:p" % rid)
    l.append(uid)
    for i in range(int(data['player']) - 1):
        l.append('')
    user_info = Hash("%s_%s" % (bid, uid))
    user_info.hset("rid", rid)
    room = Hash("r:%s" % rid)
    admin = data.get("admin")
    if not admin:
        admin = uid
    gid = data.get("gid", "")
    mid = data.get("mid", "")
    cid = data.get("cid", "")
    must_put = data.get("must_put", "y")
    black_three_first = data.get("black_three_first", "n")
    card_warning = data.get("card_warning", "1")
    end_huxi = data.get("end_huxi", "100")
    
    message = {"end_huxi":end_huxi, "must_put":must_put, "black_three_first":black_three_first, "card_warning":card_warning, "mid":mid, "gid":gid, "cid":cid, "status":"", "admin":admin, "dealer":uid, "bid":bid, "last_get_user":uid, "game_round":1, "all_round":data['all_round'], "card":data['card'], "game_type":data['game_type'], "horse":data['horse'], "grab_bar_win":data['grab_bar_win'], "grab_bar_duty":data['grab_bar_duty'], "bar_win_duty":data['bar_win_duty'], "seven_pair_win":data['seven_pair_win']}
    logging.info(message)
    room.update(message)

def join_room(reader, bid, uid, data):
    seat = int(data['seat'])
    rid = data['rid']
    l = List("r:%s:p" % rid)
    members = l.members
    l[seat - 1] = uid
    user_info = Hash("%s_%s" % (bid, uid))
    user_info.hset("rid", rid)
    location_list = data.get("location_list", [])
    for member in members:
        if member == str(uid) or not member:
            continue
        location_info = u""
        p = Hash("%s_%s" % (bid, member))
        for loc in location_list:
            pname = p['name']
            #if len(pname) > 6:
            #    pname = p['name'][:6]
            if pname not in loc.encode("utf-8"):
                location_info += loc
        #logging.info(location_info)
        data['location_info'] = location_info
        game.nsq_pub({"b":bid, "u":member, "t":"game", "d":data})
    #push_member_cast("game", members, bid, uid, data)
    return

def renotify_join_room(bid, rid):
    '''start之前通知所有玩家重新进入'''
    l = List("r:%s:p" % rid)
    members = l.members
    db = dao.Game_DB()
    for uid in members:
        if not uid:
            continue
        row = db.get_player(rid, uid)
        user_info = db.user_info(bid, uid)
        data = {"act":"join_room", "ranking":0, "name":user_info['name'], "head":user_info['head'],"sex":user_info['sex'], "seat":row['seat'], "uid":uid, "ip":row['ip'], "status":row['status'], "online":row['online'], "score":row['score'], "gps_lat":row['gps_lat'], "gps_lon":row['gps_lon']}
        for member in members:
            if member == uid:
                continue
            game.nsq_pub({"b":bid, "u":member, "t":"game", "d":data})

def ready(reader, bid, uid, data):
    user_info = Hash("%s_%s" % (bid, uid))
    if data.get("offline"):
        user_info['online'] = 'n'
    else:
        if not data.get("auto_ready"):
            user_info['online'] = 'y'
    rid = user_info['rid']
    if not rid:
        rid = data.get("rid", "")
    data['uid'] = uid
    l = List("r:%s:p" % rid)
    members = l.members
    data['act'] = 'ready'
    data['rid'] = rid
    push_room_cast("game", rid, bid, uid, data)
    # 检查是否全部就绪,开始游戏
    room = Hash("r:%s" % rid)
    db = dao.Game_DB()
    db.update_player_ready(rid, uid)
    db.commit()
    room_player_cnt = db.room_player_cnt(rid, 1)
    room_info = db.get_room(rid, bid)
    if not room_info:
        return
    if room_player_cnt['player'] == len(l):
        game_type = room['game_type']
        game_round = room_info['game_round']
        if not game_round or str(game_round) < '1':
            renotify_join_room(bid, rid)
        if room.get('status') != 'start' and room_info['flag'] == 'y' and game_round < room_info['all_round']:
            
            # 开始游戏
            logging.info(u"玩家全部准备:%s,开始游戏" % rid)
            #if not game_round or str(game_round) < '1':
            #    renotify_join_room(bid, rid)
            dealer = room.get("dealer")
            if not dealer:
                dealer = members[0]
            match_id = room['mid']
            if match_id:
                if not game_round:
                    game_round = 1
                idx = (int(game_round) - 1) % len(members)
                dealer = members[idx]
            if game_type == 'zzmj':
                tdh = game.ZZMaJiang(rid, bid, dealer)
            elif game_type == 'hzmj':
                tdh = game.HZMaJiang(rid, bid, dealer)
            elif game_type == 'csmj':
                tdh = game.CSMaJiang(rid, bid, dealer)
            elif game_type == 'pdk':
                tdh = game.PaoDeKuai(rid, bid, dealer)
            elif game_type == 'ldfpf':
                tdh = paohuzi.PaoHuZi(rid, bid, dealer)
            else:
                tdh = game.MaJiang(rid, bid, dealer)
            tdh.start()
        else:
            #已经开始过的游戏
            if not data.get("auto_ready"):
                if game_type == 'ldfpf':
                    paohuzi.get_last_data(reader, bid, uid, data)
                else:
                    game.get_last_data(reader, bid, uid, data)
    
    apply_user = room_info['apply_user']
    if room_info['dismiss_apply'] and room_info['flag'] == 'y' and str(uid) != apply_user:
        if str(uid) not in room_info['agree_user'].split(","):
            timer = int(room_info['dismiss_apply'][14:16]) - int(time.strftime("%M"))
            nsq_pub("response", {"act":"dismiss_apply", "uid":apply_user, "timer":timer})

def quit_room(reader, bid, uid, data):
    rid = data['rid']
    l = List("r:%s:p" % rid)
    members = l.members
    if uid in members:
        idx = members.index(uid)
        l[idx] = ''
    push_member_cast("game", members, bid, uid, data)
    return

def dismiss_room(reader, bid, uid, data):
    rid = data['rid']
    room = Hash("r:%s" % rid)
    l = List("r:%s:p" % rid)
    data['rid'] = rid
    members = l.members
    for member in members:
        game.nsq_pub({"b":bid, "u":str(member), "t":"game", "d":data})
    game_type = room['game_type']
    if game_type == 'zzmj':
        tdh = game.ZZMaJiang(rid, bid)
    elif game_type == 'hzmj':
        tdh = game.HZMaJiang(rid, bid)
    elif game_type == 'csmj':
        tdh = game.CSMaJiang(rid, bid)
    elif game_type == 'pdk':
        tdh = game.PaoDeKuai(rid, bid)
    elif game_type == 'ldfpf':
        tdh = paohuzi.PaoHuZi(rid, bid)
    else:
        tdh = game.MaJiang(rid, bid)
    db = dao.Game_DB()
    db.dismiss_room(rid)
    l.clear()
    room.clear()
    if room.get('status'):
        tdh.dismiss()

def dismiss_apply(reader, bid, uid, data):
    user_info = Hash("%s_%s" % (bid, uid))
    rid = user_info['rid']
    l = List("r:%s:p" % rid)
    members = l.members
    data['uid'] = uid
    data['timer'] = 5
    db = dao.Game_DB()
    room_info = db.get_room(rid, bid)
    if room_info['mid']:
        logging.error(u"比赛的房间不可解散:%s,%s,%s" % (bid, uid, rid))
        return
    #push_room_cast("game", rid, bid, uid, data)
    data['rid'] = rid
    for member in members:
        game.nsq_pub({"b":bid, "u":str(member), "t":"game", "d":data})
    if not room_info['dismiss_apply']:
        dismiss_apply_time = (datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime("%Y-%m-%d %H:%M:%S")
        db.dismiss_apply(rid, dismiss_apply_time, uid)
    return

def dismiss_agree(reader, bid, uid, data):
    user_info = Hash("%s_%s" % (bid, uid))
    rid = user_info['rid']
    db = dao.Game_DB()
    l = List("r:%s:p" % rid)
    members = l.members
    data['uid'] = uid
    data['rid'] = rid
    #push_room_cast("game", rid, bid, uid, data)
    for member in members:
        game.nsq_pub({"b":bid, "u":str(member), "t":"game", "d":data})
    db.dismiss_agree(rid, uid)
    db.commit()
    room_info = db.get_room(rid, bid)
    agree_num = room_info['dismiss_agree']
    player_num = room_info['player']
    if player_num <= (agree_num + 2):
        i = db.dismiss_room(rid)
        if room_info['game_type'] == 'zzmj':
            tdh = game.ZZMaJiang(rid, bid)
        elif room_info['game_type'] == 'hzmj':
            tdh = game.HZMaJiang(rid, bid)
        elif room_info['game_type'] == 'csmj':
            tdh = game.CSMaJiang(rid, bid)
        elif room_info['game_type'] == 'pdk':
            tdh = game.PaoDeKuai(rid, bid)
        elif room_info['game_type'] == 'ldfpf':
            tdh = paohuzi.PaoHuZi(rid, bid)
        else:
            tdh = game.MaJiang(rid, bid)
        tdh.game_over(from_dissmiss=True)
        tdh.dismiss()
        return

def dismiss_refuse(reader, bid, uid, data):
    user_info = Hash("%s_%s" % (bid, uid))
    rid = user_info['rid']
    l = List("r:%s:p" % rid)
    members = l.members
    data['uid'] = uid
    data['rid'] = rid
    #push_room_cast("game", rid, bid, uid, data)
    for member in members:
        game.nsq_pub({"b":bid, "u":str(member), "t":"game", "d":data})
    db = dao.Game_DB()
    db.dismiss_refuse(rid)
    
    
def game_act(reader, bid, uid, data):
    user_info = Hash("%s_%s" % (bid, uid))
    rid = user_info['rid']
    if not rid:
        rid = data.get("rid", "")
    room = Hash("r:%s" % rid)
    game_type = room['game_type']
    if game_type == 'zzmj':
        tdh = game.ZZMaJiang(rid, bid)
    elif game_type == 'hzmj':
        tdh = game.HZMaJiang(rid, bid)
    elif game_type == 'csmj':
        tdh = game.CSMaJiang(rid, bid)
    elif game_type == 'pdk':
        tdh = game.PaoDeKuai(rid, bid)
    elif game_type == 'ldfpf':
        tdh = paohuzi.PaoHuZi(rid, bid)
    else:
        tdh = game.MaJiang(rid, bid)
    if data['act'] == 'put_card':
        tdh.put_card(uid, data)
    elif data['act'] == 'pass':
        tdh.pass_card(uid, data)
    elif data['act'] == 'dark_bar':
        tdh.dark_bar(uid, data)
    elif data['act'] == 'pair_bar':
        tdh.pair_bar(uid, data)
    elif data['act'] == 'bar':
        tdh.bar(uid, data)
    elif data['act'] == 'pair':
        tdh.pair(uid, data)
    elif data['act'] == 'pair_bar_win':
        tdh.pair_bar_win(uid, data)
    elif data['act'] == 'win':
        tdh.win(uid, data)
    elif data['act'] == 'put_win':
        tdh.put_win(uid, data)
    elif data['act'] == 'eat':
        tdh.eat(uid, data)
    elif data['act'] == 'game_start':
        rid = data['rid']
        room = Hash("r:%s" % rid)
        dealer = room['dealer']
        game_type = room['game_type']
        if game_type == 'zzmj':
            tdh = game.ZZMaJiang(rid, bid, dealer)
        elif game_type == 'hzmj':
            tdh = game.HZMaJiang(rid, bid, dealer)
        elif game_type == 'csmj':
            tdh = game.CSMaJiang(rid, bid, dealer)
        elif game_type == 'pdk':
            tdh = game.PaoDeKuai(rid, bid, dealer)
        elif game_type == 'ldfpf':
            tdh = paohuzi.PaoHuZi(rid, bid, dealer)
        else:
            tdh = game.MaJiang(rid, bid, dealer)
        tdh.start()
    return

def get_last_data(reader, bid, uid, data):
    user_info = Hash("%s_%s" % (bid, uid))
    rid = user_info['rid']
    if not rid:
        rid = data.get("rid", "")
    room = Hash("r:%s" % rid)
    game_type = room['game_type']
    if game_type == 'zzmj':
        tdh = game.ZZMaJiang(rid, bid)
    elif game_type == 'hzmj':
        tdh = game.HZMaJiang(rid, bid)
    elif game_type == 'csmj':
        tdh = game.CSMaJiang(rid, bid)
    elif game_type == 'pdk':
        tdh = game.PaoDeKuai(rid, bid)
    else:
        tdh = game.MaJiang(rid, bid)
    last_get_user = tdh.room['last_get_user']
    last_put_user = tdh.room['last_put_user']
    pubdata = {'act':'get_last_data', 'last_player':last_get_user, 'last_put_user':last_put_user}
    all_player_data = []
    l = List("r:%s:p" % rid)
    members = l.members
    for member in members:
        p = game.Player(member, bid, rid)
        player_data = p.get_data()
        if member != uid:
            player_data['current_card'] = ["*"] * len(player_data['current_card'])
            #player_data['dark_bar'] = ["*"] * len(player_data['dark_bar'])
        all_player_data.append(player_data)
    tips = []
    dark_bar = []
    eat_card = []
    last_put_card = ''
    stop_pop_user = tdh.room['stop_pop_user']
    if stop_pop_user == str(uid):
        tips = tdh.room['stop_pop_act'].split(",")
        if tips:
            p = game.Player(uid, bid, rid)
            current_card = p.current_card.members
            
            if 'dark_bar' in tips:
                dark_bar = tdh.check_init_dark_bar(current_card)
            if "eat" in tips:
                last_put_player = game.Player(last_put_user, bid, rid)
                last_put_card = last_put_player.out_card.members[-1]
                eat_card = tdh.get_eat_card(current_card, last_put_card)
        
        if game_type == 'pdk':
            if last_put_user and last_put_user != str(uid):
                put_type = 'follow'
            else:
                put_type = 'free'
            tips = ['turn_put',]
            pubdata['last_player'] = uid
            pubdata['last_put_user'] = uid
            pubdata['put_type'] = put_type
            pubdata['card_type'] = tdh.room['last_put_type']
            pubdata['last_put_card'] = tdh.room['last_put_card'].split(",")
    
    dealer = tdh.room['dealer']
    if not dealer:
        dealer = ''
    pubdata["dealer"] = dealer 
    pubdata["tips"] = tips
    pubdata["dark_bar"] = dark_bar
    pubdata["eat_card"] = eat_card
    pubdata["card"] = last_put_card
    pubdata["left_card"] = len(tdh.current_card)
    pubdata["all_player_data"] = all_player_data
    pubdata["game_round"] = room['game_round']
    pubdata["all_round"] = room['all_round']
    game.nsq_pub({'u':str(uid), 'b':bid, 't':'game', 'd':pubdata})
    return

    
def get_new_data(reader, bid, uid, data):
    pubdata = {'act':'get_new_data'}
    mid = data.get('mid')
    user_info = Hash("%s_%s" % (bid, uid))
    rid = user_info['rid']
    sql = "SELECT `data` FROM t_response WHERE msgid> %s AND uid='%s' AND roomid='%s' AND act IN ('put_card','get_card') ORDER BY msgid ASC" % (mid, uid, rid)
    db = dao.Game_DB()
    tmplist = db.query(sql)
    pubdata['datalist'] = tmplist
    game.nsq_pub({'u':str(uid), 'b':bid, 't':'game', 'd':pubdata})
    return 

def job_dismiss_room():
    db = dao.Game_DB()
    room_list = db.expire_room()
    for room in room_list:
        rid = room['rid']
        bid = room['bid']
        db.dismiss_room(rid)
        card = room['card']
        uid = room['admin']
        db.deduct_card(bid, uid, card)
        if room['game_type'] == 'zzmj':
            tdh = game.ZZMaJiang(rid, bid)
        elif room['game_type'] == 'hzmj':
            tdh = game.HZMaJiang(rid, bid)
        elif room['game_type'] == 'csmj':
            tdh = game.CSMaJiang(rid, bid)
        elif room['game_type'] == 'pdk':
            tdh = game.PaoDeKuai(rid, bid)
        else:
            tdh = game.MaJiang(rid, bid)
        tdh.game_over(from_dissmiss=True)
        tdh.dismiss()

def start_match(reader, bid, uid, data):
    rid = data['rid']
    db = dao.Game_DB()
    exp_time = (datetime.datetime.now() + datetime.timedelta(seconds=15)).strftime("%Y-%m-%d %H:%M:%S")
    db.add_cron(bid, 'auto_ready', rid, 0, exp_time)
    #l = List("r:%s:p" % rid)
    #members = l.members
    #for uid in members:
    #    user_info = Hash("%s_%s" % (bid, uid))
    #    if user_info['online'] != 'y':
    #        data['offline'] = 'y'
    #        ready(reader, bid, uid, data)

def stop_match(reader, bid, uid, data):
    mid = data['mid']
    db = dao.Game_DB()
    rows = db.query("select * from t_room where mid=%s and flag='y'" % mid)
    for room in rows:
        bid = room['bid']
        rid = room['rid']
        if room['game_type'] == 'zzmj':
            tdh = game.ZZMaJiang(rid, bid)
        elif room['game_type'] == 'hzmj':
            tdh = game.HZMaJiang(rid, bid)
        elif room['game_type'] == 'csmj':
            tdh = game.CSMaJiang(rid, bid)
        elif room['game_type'] == 'pdk':
            tdh = game.PaoDeKuai(rid, bid)
        else:
            tdh = game.MaJiang(rid, bid)
        tdh.game_over(from_dissmiss=True)
        tdh.dismiss()
        

def client_report(reader, bid, uid, data):
    db = dao.Game_DB()
    ip = data.get('ip')
    if not ip:
        return
    port = data.get('port', 8098)
    client = data.get('total', 0)
    row = db.get_connserver(ip, port)
    if row:
        db.update_connserver(ip, port, client)
    else:
        db.add_connserver(ip, port, client)

def job_cron():
    db = dao.Game_DB()
    db.execute("update t_game set status=3 where status=0 and ctime<now()-interval 8 hour")
    rows = db.query("select * from t_cron where exp_time<now()")
    for row in rows:
        bid = row['bid']
        act = row['act']
        rid = row['rid']
        uid = row['uid']
        if act == 'auto_ready':
            if rid:
                l = List("r:%s:p" % rid)
                members = l.members
                for uid in members:
                    ready('', bid, uid, {"rid":rid, "act":"ready", "auto_ready":"y"})
            elif uid:
                ready('', bid, uid, {"rid":"", "act":"ready", "auto_ready":"y"})
                
            db.execute("delete from t_cron where id=%s" % row['id'])
        logging.info(u"处理超时任务:%s, %s, %s, %s" % (bid, act, rid, uid))
